﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using CommonHelper;
using CommonLibrary;
using CommonSite.Areas.API.Models;
using CommonLibrary.Models;
using BLToolkit.Data;
using BLToolkit.Data.Linq;
using BLToolkit.DataAccess;
using System.Text;

namespace CommonSite.Areas.API.Controllers
{
    public class NodeController : BaseAPIController
    {
        public ActionResult Query(string id)
        {
            int flowId = CryptHelper.DecryptToInt32(id);
            using (CommonDatabase db = new CommonDatabase())
            {
                var query = GetNodes(db, flowId);
                return Success(new { Rows = query });
            }
        }

        public ActionResult GetUsers()
        {
            using (CommonDatabase db = new CommonDatabase())
            {
                var query = (from p in db.sys_UserOrgPosition
                             select new UserOrgPosition
                             {
                                 UserId = CryptHelper.Encrypt(p.UserId),
                                 UserName = p.sys_User.sys_UserInfo.UserName,
                                 OrgId = CryptHelper.Encrypt(p.OrgId),
                                 Org = p.sys_Org.Name,
                                 PositionId = CryptHelper.Encrypt(p.PositionId),
                                 Position = p.sys_Position.Name,
                                 ParentUserId = p.ParentUserId == null ? null : CryptHelper.Encrypt(p.ParentUserId),
                                 ParentUserName = p.ParentUser.sys_UserInfo.UserName
                             }).ToList();

                return Success(new
                {
                    Total = query.Count,
                    Rows = query
                });
            }
        }
        
        public ActionResult Save(string flow, string data)
        {
            if (!flow.IsNullOrWhiteSpace() &&
                !data.IsNullOrWhiteSpace())
            {                
                try
                {
                    var flowId = CryptHelper.DecryptToInt32(flow);
                    var list = JSON.ToObject<List<_Node>>(data);
                    using (CommonDatabase db = new CommonDatabase(CommonOption.ReadWrite))
                    {
                        db.BeginTransaction();
                        if (db.wf_Flow.Where(k => k.Id == flowId).Count() == 0)
                            return ArgumentFailed();

                        // 删除现有节点
                        db.wf_Node.Where(k => k.FlowId == flowId).Set(k => k.State, 0).Update();
                        #region 添加节点
                        List<wf_Node> nodes = new List<wf_Node>();
                        foreach (var item in list)
                        {
                            wf_Node node = new wf_Node() 
                            {
                                ActionId = item.Action,
                                Name = item.Name,
                                FlowId = flowId
                            };
                            switch (item.Action)
                            {
                                case 1:
                                case 2:
                                    OnUserMode(node, item);
                                    break;

                                case 3:
                                    OnRoleMode(node, item);
                                    break;

                                case 4:
                                    OnParentUserMode(node, item);
                                    break;

                                default:
                                    throw new ArgumentException("参数错误");
                            }

                            node.Id = Convert.ToInt32(db.InsertWithIdentity(node));
                            nodes.Add(node);
                        }
                        #endregion

                        #region 添加节点流转顺序
                        var transitions = new List<wf_Transition>();

                        if (nodes.Count > 0)
                        {
                            if (nodes.Count == 1)
                            {
                                // 开始、结束相同
                                transitions.Add(new wf_Transition()
                                {
                                    FlowId = flowId,
                                    NodeId = nodes[0].Id,
                                    PreNodeId = null,
                                    NextNodeId = null
                                });
                            }
                            else
                            {
                                // 开始节点
                                transitions.Add(new wf_Transition()
                                {
                                    FlowId = flowId,
                                    NodeId = nodes[0].Id,
                                    PreNodeId = null,
                                    NextNodeId = nodes[1].Id
                                });

                                for (int i = 1; i < nodes.Count - 1; ++i)
                                {
                                    transitions.Add(new wf_Transition()
                                    {
                                        FlowId = flowId,
                                        NodeId = nodes[i].Id,
                                        PreNodeId = nodes[i - 1].Id,
                                        NextNodeId = nodes[i + 1].Id
                                    });
                                }

                                // 结束节点
                                transitions.Add(new wf_Transition()
                                {
                                    FlowId = flowId,
                                    NodeId = nodes[nodes.Count - 1].Id,
                                    PreNodeId = nodes[nodes.Count - 2].Id,
                                    NextNodeId = null
                                });
                            }
                        }

                        if (transitions.Count > 0)
                            db.InsertBatch(transitions);
                        #endregion

                        db.CommitTransaction();
                    }

                    return Success();
                }
                catch (Exception ex)
                {
                    return Failed(ex.Message);
                }
            }

            return ArgumentFailed();
        }

        private void OnUserMode(wf_Node node, _Node item)
        {
            if (item.Action == 1)
            {
                node.ToUser = CryptHelper.DecryptToInt32(item.UserId);
                node.ToDept = CryptHelper.DecryptToInt32(item.OrgId);
                node.ToPosition = CryptHelper.DecryptToInt32(item.PositionId);
            }
            else if (item.Action == 2)
            {
                var arg =
                    "{" +
                    string.Join(
                        ",",
                        item.Argument.Select(
                            k => CryptHelper.DecryptToInt32(k.UserId)).ToArray()
                    ) +
                    "}";
                node.Argument = arg;
            }
        }

        private void OnRoleMode(wf_Node node, _Node item)
        {
            node.ToRole = CryptHelper.DecryptToInt32(item.RoleId);
            node.InDept = item.RoleInDept;
        }

        private void OnParentUserMode(wf_Node node, _Node item)
        {
            // Nothing to do here.
        }

        private List<_Node> GetNodes(CommonDatabase db, int flowId)
        {
            var list = new List<_Node>();
            var users = db.sys_UserInfo.ToDictionary(k => k.UserId);
            var positions = db.sys_Position.ToDictionary(k => k.Id);
            var orgs = db.sys_Org.ToDictionary(k => k.Id);


            var query = (from p in db.wf_Node
                         where p.FlowId == flowId && p.State == 1
                         select p).ToList();

            foreach (var p in query)
            {
                switch (p.ActionId)
                {
                    case 1:
                        list.Add(new _Node
                        {
                            Action = 1,
                            Name = p.Name,
                            OrgId = CryptHelper.Encrypt(p.ToDept),
                            Org = orgs[p.ToDept.Value].Name,
                            PositionId = CryptHelper.Encrypt(p.ToPosition),
                            Position = positions[p.ToPosition.Value].Name,
                            UserId = CryptHelper.Encrypt(p.ToUser),
                            UserName = users[p.ToUser.Value].UserName
                        });
                        break;

                    case 2:
                        var split = p.Argument.Trim('{', '}').Split(',');
                        var arg = from s in split
                                  select new ApprovalUser
                                  {
                                      UserId = CryptHelper.Encrypt(s),
                                      UserName = users[int.Parse(s)].UserName
                                  };
                        list.Add(new _Node
                        {
                            Action = 2,
                            Name = p.Name,
                            Argument = arg.ToList()
                        });
                        break;

                    case 3:
                        list.Add(new _Node
                        {
                            Action = 3,
                            Name = p.Name,
                            RoleId = CryptHelper.Encrypt(p.ToRole),
                            RoleInDept = p.InDept,
                            Role = db.sys_Role.Where(k => k.Id == p.ToRole.Value).First().Name
                        });
                        break;

                    case 4:
                        list.Add(new _Node 
                        {
                            Name = p.Name,
                            Action = 4
                        });
                        break;
                }
            }

            return list;
        }
    }
}
